﻿using System;
using System.Collections.Concurrent;
using System.Net.Http;
using System.Threading.Tasks;
using IdentityModel.Client;
using Shared.Service.Http;

namespace Infrastructure.ExternalService.Core.Imp
{
    public class OrderingAccessTokenProvider : AccessTokenProvider
    {
        private readonly string _tokenEndpoint;

        public OrderCenterApiOptions Options { get; }

        private static readonly ConcurrentDictionary<string, AccessToken> Tokens =
            new ConcurrentDictionary<string, AccessToken>();

        public OrderingAccessTokenProvider(OrderCenterApiOptions options, IDiscoveryCache discoveryCache)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            if (string.IsNullOrEmpty(options.AuthenticationOptions.EndPoint))
            {
                throw new Exception($"认证服务器地址“{nameof(options.AuthenticationOptions.EndPoint)}”未配置");
            }

            if (string.IsNullOrEmpty(options.ClientId))
            {
                throw new Exception($"认证服务器“{nameof(options.ClientId)}”未配置");
            }

            if (string.IsNullOrEmpty(options.ClientSecret))
            {
                throw new Exception($"认证的服务器密钥{nameof(options.ClientSecret)}未配置");
            }

            //使用依赖注入的缓存
            var discoveryResponse = discoveryCache.GetAsync().Result;
            if (discoveryResponse.IsError)
            {
                throw new Exception(discoveryResponse.Error);
            }

            _tokenEndpoint = discoveryResponse.TokenEndpoint;
            Options = options;
        }

        public override async Task<string> GetAccessToken(string scope)
        {
            Tokens.TryGetValue(scope, out AccessToken token);

            if (token != null && !token.IsExpired())
            {
                return await Task.FromResult(token.Token);
            }

            using (var client = new HttpClient())
            {
                var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
                {
                    Address = _tokenEndpoint,

                    ClientId = Options.ClientId,
                    ClientSecret = Options.ClientSecret,
                    Scope = scope
                });

                if (response.IsError)
                {
                    throw new Exception(response.ErrorDescription ?? response.Error);
                }

                token = new AccessToken(response.AccessToken, response.ExpiresIn);
                Tokens.TryAdd(scope, token);
            }

            return await Task.FromResult(token.Token);
        }
    }
}
